#include "BKAPI.h"

using namespace BKAPI;

void BKMouse::Click()
{
	typedef void (* MouseEvents)(LONG, LONG, LONG, LONG, LONG);
	
	HINSTANCE User32;
	MouseEvents MClick;
	
	User32 = LoadLibrary("user32");
	if (User32 != NULL)  //Successfully opened DLL
	{
		MouseEvents MClick;
		MClick = (MouseEvents)GetProcAddress(User32, "mouse_event");
		
		if (!MClick)	//Failed to get function
		{
			FreeLibrary(User32);
			throw -1;
		}
		else
		{
			MClick(0x2, 0, 0, 0, 0);
			MClick(0x4, 0, 0, 0, 0);
		}
	}
}

void BKMouse::ClickAtPoint(int xPos, int yPos)
{
	SetCursorPos(xPos, yPos);
	this->Click();
}

void BKMouse::ClickAtPoint(int xPos, int yPos, bool isRelative)
{
	if (isRelative)
	{
        LPPOINT currPos;
        GetCursorPos(currPos);
        SetCursorPos(currPos->x + xPos, currPos->y + yPos);
        this->Click();
	}
    else
	{
        SetCursorPos(xPos, yPos);
        this->Click();
    }
}

void BKMouse::GetMouseXY(int &xPos, int &yPos)
{
	LPPOINT currPos;
	GetCursorPos(currPos);
	
	xPos = currPos->x;
	yPos = currPos->y;
}

void BKMouse::SetMouseXY(int xPos, int yPos)
{
	SetCursorPos(xPos, yPos);
}

HWND BKWindow::GetWindow(std::string windowTitle)
{	
	return FindWindow(NULL, windowTitle.c_str());	//Returns NULL if failed
}

HWND BKWindow::GetWindow(std::string windowTitle, bool shouldThrowError)
{
	HWND windHandle = FindWindow(NULL, windowTitle.c_str());
	
	if (shouldThrowError)
	{
		if (windHandle == NULL) 
		{
			DWORD error = GetLastError();
			if (error == 0)
			{
				std::string message = std::string("Couldn't find window with title: ") + windowTitle;
				MessageBox(NULL, (LPCTSTR)message.c_str(), (LPCTSTR)"Error", MB_OK | MB_ICONEXCLAMATION);
			}
			else
			{
				BKAPI::BKError error;
				error.ShowDefaultErrorMessage((LPSTR)"FindWindow");
			}
		}
	}
	
	return windHandle;		//Returns NULL if failed
}

bool BKWindow::GetWindowRectangle(HWND handleOfWindow, LPRECT rectangleToStoreIn)
{
	return GetWindowRect(handleOfWindow, rectangleToStoreIn);			//Returns true if successful
}

bool BKWindow::GetWindowRectangle(std::string windowTitle, LPRECT rectangleToStoreIn)
{
	return GetWindowRect(GetWindow(windowTitle), rectangleToStoreIn);	// Returns true if successful
}

void BKWindow::MakeSimpleWindow(WNDPROC WindowProcedure, LPCTSTR WindowTitle)
{
	MakeWindow(0, WindowProcedure, 0, (HBRUSH) COLOR_APPWORKSPACE, (LPCTSTR)"simpleBKWindow", 0, WindowTitle);
}

void BKWindow::MakeWindow(UINT WindowStyle, WNDPROC WindowProcedure, HINSTANCE hInstance, HBRUSH BackgroundColor, LPCTSTR ClassName, DWORD WindowType, LPCTSTR TitleText)
{
	WNDCLASSEX wc;
	HWND hwnd;
	MSG Msg;

	////////////////////////////////////////////////
	//    Step 1: Registering the Window Class    //
	////////////////////////////////////////////////
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = WindowStyle;// CS_NOCLOSE;
	wc.lpfnWndProc = WindowProcedure;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = BackgroundColor; //(HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = ClassName;
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	
	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, "Window Registration Failed!", "Error!",
		MB_ICONEXCLAMATION | MB_OK);
		
		throw -1;
	}


	///////////////////////////////////////
	//    Step 2: Creating the Window    //
	///////////////////////////////////////
	hwnd = CreateWindowEx(WindowType, ClassName, TitleText, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL);

	if(hwnd == NULL)
	{
		MessageBox(NULL, "Window Creation Failed!", "Error!",
		MB_ICONEXCLAMATION | MB_OK);
		
		throw -1;
	}

	ShowWindow(hwnd, 1);
	UpdateWindow(hwnd);

	////////////////////////////////////
	//    Step 3: The Message Loop    //
	////////////////////////////////////
	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
}

int BKNotification::MsgBox(std::string message, std::string caption, unsigned msgStyle)
{
	int retVal = MessageBox(NULL, (LPCTSTR)message.c_str(), (LPCTSTR)caption.c_str(), msgStyle);
}

int BKNotification::MsgBox(std::string message, std::string caption)
{
	int retVal = MessageBox(NULL, (LPCTSTR)message.c_str(), (LPCTSTR)caption.c_str(), MB_OK);
}

void BKSystem::Pause()
{
	std::cout << "Press ENTER to continue...";
	std::cin.clear();
	std::cin.sync();
	std::cin.get();
}

std::string BKConvert::LongToString(long value)
{
	std::string number;
	std::stringstream strstream;
	strstream << value;
	strstream >> number;
	return number;
}

std::string BKFileSystem::CurrentDirectory()
{
	const unsigned len = GetCurrentDirectory(0, 0);
	
	if (!len) return "";
	
	std::string dir(len, '\0');
	
	if (!GetCurrentDirectory(len, &dir[0])) return "";
	
	return dir;
}

bool BKFileSystem::FileExists(const char *filePath)
{
	std::ifstream fileToCheck(filePath);	// Tries to open the file...
	return fileToCheck;				// Returns TRUE if file can open ('return' casts to boolean)
}

std::string BKFileSystem::GetAvailableDriveStrings(bool shouldIncludeType)
{
	char * driveList;
	std::string sList;
	
	int driveType = 99;
	char drivesAvailable[256];
	char *temp = drivesAvailable;
	
	GetLogicalDriveStrings(256, drivesAvailable);
	
	if (shouldIncludeType)
	{		
			//for(int i = 0; i < strlen(temp) * 4; i = i + 4)
			for(int i = 0; i < 15; i =  i + 4)
			{
				std::string tempDrive = std::string(&temp[i]);
				
				driveType = GetDriveType(tempDrive.c_str());
				
				switch(driveType)
				{
					case 0: // Unknown
						sList += std::string(&temp[i]) + std::string(" : Unknow Drive type\n");
						break;
					case 1: // Invalid
						sList += std::string(&temp[i]) + std::string(" : Drive is invalid\n");
						break;
					case 2: // Removable Drive
						sList += std::string(&temp[i]) + std::string(" : Removable Drive\n");
						break;
					case 3: // Fixed
						sList += std::string(&temp[i]) + std::string(" : Hard Disk (Fixed)\n");
						break;
					case 4: // Remote
						sList += std::string(&temp[i]) + std::string(" : Remote (Network) Drive\n");
						break;
					case 5: // CDROM
						sList += std::string(&temp[i]) + std::string(" : CD-Rom/DVD-Rom\n");
						break;
					case 6: // RamDrive
						sList += std::string(&temp[i]) + std::string(" : Ram Drive\n");
						break;
				}
			}
	}
	else
	{
		for(int i = 0; i < 15; i =  i + 4)
		{
			sList += (std::string)&temp[i] + (std::string)"\n";
		}
	}
	
	return sList;
}

void BKFileSystem::PrintAvailableDrives(bool shouldIncludeType)
{
	char * driveList;
	
	int driveType = 99;
	char drivesAvailable[256];
	char *temp = drivesAvailable;
	
	HANDLE hDrive;
	
	GetLogicalDriveStrings(256, drivesAvailable);
	
	if (shouldIncludeType)
	{
		while(*temp)
		{
			driveType = GetDriveType(temp);
			
			switch(driveType)
			{
				case 0: // Unknown
					printf("%s : Unknown Drive type\n",temp);
					break;
				case 1: // Invalid
					printf("%s : Drive is invalid\n",temp);
					break;
				case 2: // Removable Drive
					printf("%s : Removable Drive\n",temp);
					break;
				case 3: // Fixed
					printf("%s : Hard Disk (Fixed)\n",temp);
					break;
				case 4: // Remote
					printf("%s : Remote (Network) Drive\n",temp);
					break;
				case 5: // CDROM
					printf("%s : CD-Rom/DVD-Rom\n",temp);
					break;
				case 6: // RamDrive
					printf("%s : Ram Drive\n",temp);
					break;
			}
			
			temp += lstrlen(temp) + 1;
		}
	}
	else
	{
		while(*temp)
		{
			printf("%s\n", temp);

			temp += lstrlen(temp) + 1;
		}
	}
}

void BKError::ShowDefaultErrorMessage(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    sprintf((LPTSTR)lpDisplayBuf, TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK | MB_ICONEXCLAMATION); 
	
	//printf("%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}